﻿Namespace Magic

    Module mMagics

        Public LastDir As EvoLib.Protocol.Direction


        Public Structure MagicDesc

            Dim imglib As EvoLib.Protocol.ImgLibs
            Dim startidx As Integer
            Dim count As Integer
            Dim looping As Integer
            Dim speed As Long

            Dim sound As String

            Dim currentimg As Integer
            Dim ticker As Long

            Dim x As Integer
            Dim y As Integer

            Dim xoff As Double
            Dim yoff As Double
            Dim moving As Boolean
            Dim steps As Integer
            Dim deststeps As Integer
            Dim speedx As Double
            Dim speedy As Double

            Dim code As Integer

            Dim targetx As Integer
            Dim targety As Integer
            Dim hittargetx As Boolean
            Dim hittargety As Boolean

            Dim act As Boolean

            Dim charid As Int64

            Dim dir As Direction

        End Structure

        Dim lstMagics As New List(Of MagicDesc)

        Public PreviousMagicTargetType As EvoLib.Protocol.TargetType
        Public PreviousMagicTargetIndex As Int64 = 0

        Public Sub DrawMagics(ByVal mode As Globals.DrawingMode, ByRef offsetX As Integer, ByRef offsetY As Integer, ByRef originX As Integer, ByRef originY As Integer, ByRef z As Single, ByRef tc As Long)

            If mode = Globals.DrawingMode.Magic Then

                Dim x As Integer, y As Integer
                Dim travelx As Integer, travely As Integer
                Dim tmp As MagicDesc
                Dim startidx As Integer = 0

rescan:

                For i As Integer = startidx To lstMagics.Count - 1

                    tmp = lstMagics(i)

                    travelx = (tmp.speedx * tmp.steps)

                    x = ((tmp.x - originX) * Constants.TileWidth) + offsetX + tmp.xoff + Libs.GetLib(tmp.imglib).Image(tmp.startidx + tmp.currentimg).Header.OffsetX + travelx

                    travely = (tmp.speedy * tmp.steps)

                    y = ((tmp.y - originY) * Constants.TileHeight) + offsetY + tmp.yoff + Libs.GetLib(tmp.imglib).Image(tmp.startidx + tmp.currentimg).Header.OffsetY + travely

                    Libs.GetLib(tmp.imglib).Image(tmp.startidx + tmp.currentimg).Draw(x, y, z)

                    If tmp.ticker <= tc Then
                        tmp.ticker += tmp.speed
                        tmp.steps += 1
                        tmp.currentimg += 1
                        If tmp.currentimg = tmp.count Then
                            If tmp.looping Then
                                tmp.currentimg = 0
                            Else
                                If tmp.act and tmp.charid = player.PlayerID Then
                                    ActMagic(tmp)
                                End If

                                lstMagics.RemoveAt(i)

                                If i >= lstMagics.Count Then
                                    Exit Sub
                                Else
                                    startidx = i
                                    GoTo rescan
                                End If
                            End If
                        End If

                        If tmp.moving Then

                            If tmp.steps > tmp.deststeps Then
                                lstMagics.RemoveAt(i)

                                If i >= lstMagics.Count Then
                                    Exit Sub
                                Else
                                    startidx = i
                                    GoTo rescan
                                End If

                            End If

                        End If

                        If Not tmp.sound = Nothing Then
                            Sound.PlaySoundEffect(tmp.sound, 100)
                            tmp.sound = Nothing
                        End If
                        
                        lstMagics(i) = tmp

                    End If

                Next

            End If

        End Sub


        Private Function GetAngleFromXYDiff(ByVal dx As Integer, ByVal dy As Integer) As Single

            Dim angle As Single

            If dy > 0 Then

                If dx > 0 Then


                    angle = 360 - (Math.Atan(dx / dy) * (180 / Globals.PI))

                Else

                    angle = (Math.Atan((dx * -1) / dy) * (180 / Globals.PI))

                End If

            Else

                If dx > 0 Then

                    angle = 180 + (Math.Atan(dx / (dy * -1)) * (180 / Globals.PI))
                Else
                    angle = 180 - (Math.Atan((dx * -1) / (dy * -1)) * (180 / Globals.PI))

                End If

            End If

            Return angle

        End Function


        Private Function GetDirFromXYDiff(ByVal dx As Integer, ByVal dy As Integer) As EvoLib.Protocol.Direction

            Dim dir As EvoLib.Protocol.Direction
            Dim angle As Single = GetAngleFromXYDiff(dx, dy)

            If angle > 337.5 Or angle <= 22.5 Then
                dir = EvoLib.Protocol.Direction.N

            ElseIf angle > 22.5 And angle <= 67.5 Then
                dir = EvoLib.Protocol.Direction.NE

            ElseIf angle > 67.5 And angle <= 112.5 Then
                dir = EvoLib.Protocol.Direction.E

            ElseIf angle > 112.5 And angle <= 157.5 Then
                dir = EvoLib.Protocol.Direction.SE

            ElseIf angle > 157.5 And angle <= 202.5 Then
                dir = EvoLib.Protocol.Direction.S

            ElseIf angle > 202.5 And angle <= 247.5 Then
                dir = EvoLib.Protocol.Direction.SW

            ElseIf angle > 247.5 And angle <= 292.5 Then
                dir = EvoLib.Protocol.Direction.W

            Else
                dir = EvoLib.Protocol.Direction.NW

            End If

            Return dir


        End Function

        Public Sub AddMagic(ByVal magic As EvoLib.Protocol.AddMagic, Optional ByVal act As Boolean = True)

            Dim tmp As MagicDesc
            Dim dx As Integer, dy As Integer
            Dim dx2 As Integer, dy2 As Integer
            Dim ratiox As Single, ratioy As Single
            tmp.dir = magic.dir

            tmp.code = magic.magiccode
            tmp.act = act
            tmp.charid = magic.CharID

            Select Case magic.magiccode

                Case Protocol.Magics.Teleport
                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.mProtocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.Teleport
                    tmp.count = EvoLib.Protocol.MagicAnimCount.Teleport
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.Teleport
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed

                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.sound = "109.wav"

                    lstMagics.Add(tmp)

                Case Protocol.Magics.LevelUp
                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.mProtocol.ImgLibs.GameInter
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.LevelUp1
                    tmp.count = EvoLib.Protocol.MagicAnimCount.LevelUp1
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.LevelUp
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.x = magic.startx
                    tmp.y = magic.starty
                    tmp.sound = ""

                    lstMagics.Add(tmp)

                    tmp = New MagicDesc

                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.mProtocol.ImgLibs.GameInter
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.LevelUp2
                    tmp.count = EvoLib.Protocol.MagicAnimCount.LevelUp2
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.LevelUp
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.x = magic.startx
                    tmp.y = magic.starty
                    tmp.sound = ""

                    lstMagics.Add(tmp)

                Case Magics.HealingCast
                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.HealingCast '+ (dir * 10)
                    tmp.count = EvoLib.Protocol.MagicAnimCount.HealingCast
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.HealingCast
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed

                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.sound = ""

                    lstMagics.Add(tmp)

                Case Magics.HealingEffect
                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.HealingEffect '+ (dir * 10)
                    tmp.count = EvoLib.Protocol.MagicAnimCount.HealingEffect
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.HealingEffect
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed

                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.sound = ""

                    lstMagics.Add(tmp)


                Case EvoLib.Protocol.Magics.AdamantineFireballCast, EvoLib.Protocol.Magics.MeteorShowerCast, Protocol.Magics.FireballCast
                    tmp.code = magic.magiccode
                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.AdamantineFireballCast + (tmp.dir * 10)
                    tmp.count = EvoLib.Protocol.MagicAnimCount.AdamantineFireballCast
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.AdamantineFireballCast
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed

                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.targetx = magic.destx
                    tmp.targety = magic.desty

                    tmp.sound = "M1-1.wav"

                    lstMagics.Add(tmp)

                Case EvoLib.Protocol.Magics.FireballProjectile

                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.count = EvoLib.Protocol.MagicAnimCount.FireballProjectile
                    tmp.looping = True
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.FireballProjectile
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.targetx = magic.destx
                    tmp.targety = magic.desty
                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.sound = "M1-2.wav"

                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.FireballProjectile + (CalcDirection16(magic) * 10)
                    CalcProjectileSpeedXY(tmp, magic, EvoLib.Protocol.MagicAnimSpeedXY.FireballProjectile)

                    lstMagics.Add(tmp)

                Case EvoLib.Protocol.Magics.AdamantineFireballProjectile, EvoLib.Protocol.Magics.MeteorShowerProjectile
                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    LastDir = tmp.dir
                    tmp.count = EvoLib.Protocol.MagicAnimCount.AdamantineFireballProjectile
                    tmp.looping = True
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.AdamantineFireballProjectile
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.targetx = magic.destx
                    tmp.targety = magic.desty
                    tmp.x = magic.startx
                    tmp.y = magic.starty

                    tmp.sound = "M1-2.wav"

                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.AdamantineFireballProjectile + (CalcDirection16(magic) * 10)
                    CalcProjectileSpeedXY(tmp, magic, EvoLib.Protocol.MagicAnimSpeedXY.AdamantineFireballProjectile)

                    lstMagics.Add(tmp)
                    
                Case EvoLib.Protocol.Magics.FireballHit
                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.FireballHit
                    tmp.count = EvoLib.Protocol.MagicAnimCount.FireballHit
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.FireballHit
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.x = magic.startx
                    tmp.y = magic.starty
                    tmp.sound = "M1-3.wav"
                    lstMagics.Add(tmp)

                Case EvoLib.Protocol.Magics.AdamantineFireballHit

                    tmp.imglib = EvoLib.Protocol.ImgLibs.Magic
                    tmp.startidx = EvoLib.Protocol.MagicAnimIndex.AdamantineFireballHit
                    tmp.count = EvoLib.Protocol.MagicAnimCount.AdamantineFireballHit
                    tmp.looping = False
                    tmp.speed = EvoLib.Protocol.MagicAnimSpeed.AdamantineFireballHit
                    tmp.ticker = DateTime.Now.Ticks + tmp.speed
                    tmp.x = magic.startx
                    tmp.y = magic.starty
                    tmp.sound = "M1-3.wav"
                    lstMagics.Add(tmp)

                Case EvoLib.Protocol.Magics.Slaying

                    With tmp
                        .imglib = ImgLibs.Magic
                        .startidx = Protocol.MagicAnimIndex.Slaying + (magic.dir * 10)
                        .count = Protocol.MagicAnimCount.Slaying
                        .looping = False
                        .speed = Protocol.MagicAnimSpeed.Slaying
                        tmp.ticker = DateTime.Now.Ticks + tmp.speed
                        tmp.x = magic.startx
                        tmp.y = magic.starty
                        If Characters.GetGenderFromID(magic.CharID) = Gender.Male Then
                            tmp.sound = "M7-1.wav"
                        Else
                            tmp.sound = "M7-2.wav"
                        End If

                    End With

                    lstMagics.Add(tmp)

            End Select

        End Sub

        Public Sub AddCastMagic(ByVal code As Integer, ByVal dir As EvoLib.Protocol.Direction, Optional ByVal targettype As Player.TargetType = Player.TargetType.Mob, Optional ByVal targetidx As Integer = 0)

            Dim tmp As EvoLib.Protocol.AddMagic

            Select Case code

                Case EvoLib.Protocol.Magics.AdamantineFireballCast, EvoLib.Protocol.Magics.MeteorShowerCast, Magics.HealingCast, Protocol.Magics.FireballCast
                    tmp.magiccode = code
                    tmp.dir = dir
                    tmp.startx = GameWindow.CoordX
                    tmp.starty = GameWindow.CoordY
                    tmp.destx = GameWindow.MouseX
                    tmp.desty = GameWindow.MouseY
                    tmp.CharID = Player.PlayerID

                    AddMagic(tmp)

                    Dim Packet As EvoLib.Protocol.Packet
                    Dim struct As EvoLib.Protocol.MagicCast
                    Packet.PacketType = EvoLib.Protocol.GamePacketType.Magic
                    Packet.PacketOpCode = EvoLib.Protocol.PacketOpCode.Enquiry
                    Packet.Username = Globals.sUsername

                    struct.magiccode = code
                    struct.dir = dir
                    struct.target_type = Protocol.TargetType.Coords
                    struct.targetparam1 = GameWindow.MouseX
                    struct.targetparam2 = GameWindow.MouseY

                    ClientSock.SendData(EvoLib.Protocol.MakePacket(Packet, struct, Globals.Crypto))

            End Select


        End Sub

        ''' <summary>
        ''' Sends data to the server to cast a magic
        ''' </summary>
        ''' <param name="desc">MagicDesc struct holding information on the magic</param>
        ''' <remarks></remarks>
        Private Sub ActMagic(ByVal desc As MagicDesc)

            If desc.charid <> Player.PlayerID Then Exit Sub

            'setup Magic Enquiry EvoLib.Protocol.Packet
            Dim Packet As EvoLib.Protocol.Packet
            Packet.PacketType = EvoLib.Protocol.GamePacketType.Magic
            Packet.PacketOpCode = EvoLib.Protocol.PacketOpCode.Enquiry
            Packet.Username = Globals.sUsername

            'MagicCast struct to hold the information to be sent
            Dim struct As EvoLib.Protocol.MagicCast
            struct.dir = desc.dir

            Select Case desc.code

                Case EvoLib.Protocol.Magics.FireballCast
                    struct.magiccode = EvoLib.Protocol.Magics.FireballProjectile
                    'check for mouse over specific mob
                    If Mouse.MouseOverMobIndex > 0 Then
                        'mouse is over a mob, target that
                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mouse.MouseOverMobIndex

                        'remember this target
                        PreviousMagicTargetType = struct.target_type
                        PreviousMagicTargetIndex = struct.targetparam1

                    ElseIf Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY) > 0 Then

                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY)

                        'remember this target
                        PreviousMagicTargetType = struct.target_type
                        PreviousMagicTargetIndex = struct.targetparam1

                    Else 'mouse not over a valid target

                        'check if previous target is alive
                        If IsPreviousMagicTargetAlive() Then
                            'target previous target
                            struct.target_type = PreviousMagicTargetType
                            struct.targetparam1 = PreviousMagicTargetIndex

                        Else
                            'target coords
                            struct.target_type = EvoLib.Protocol.TargetType.Coords
                            struct.targetparam1 = desc.targetx
                            struct.targetparam2 = desc.targety
                        End If
                    End If

                    'Cast an Adamantine fireball
                Case EvoLib.Protocol.Magics.AdamantineFireballCast
                    struct.magiccode = EvoLib.Protocol.Magics.AdamantineFireballProjectile
                    'check for mouse over specific mob
                    If Mouse.MouseOverMobIndex > 0 Then
                        'mouse is over a mob, target that
                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mouse.MouseOverMobIndex

                        'remember this target
                        PreviousMagicTargetType = struct.target_type
                        PreviousMagicTargetIndex = struct.targetparam1

                    ElseIf Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY) > 0 Then

                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY)

                        'remember this target
                        PreviousMagicTargetType = struct.target_type
                        PreviousMagicTargetIndex = struct.targetparam1

                    Else 'mouse not over a valid target

                        'check if previous target is alive
                        If IsPreviousMagicTargetAlive() Then
                            'target previous target
                            struct.target_type = PreviousMagicTargetType
                            struct.targetparam1 = PreviousMagicTargetIndex

                        Else
                            'target coords
                            struct.target_type = EvoLib.Protocol.TargetType.Coords
                            struct.targetparam1 = desc.targetx
                            struct.targetparam2 = desc.targety
                        End If
                    End If

                Case EvoLib.Protocol.Magics.HealingCast
                    struct.magiccode = EvoLib.Protocol.Magics.HealingEffect

                    struct.target_type = EvoLib.Protocol.TargetType.Coords
                    struct.targetparam1 = GameWindow.MouseX
                    struct.targetparam2 = GameWindow.MouseY

                Case EvoLib.Protocol.Magics.MeteorShowerCast
                    struct.magiccode = EvoLib.Protocol.Magics.MeteorShowerProjectile
                    If Mouse.MouseOverMobIndex > 0 Then
                        'mouse is over a mob, target that
                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mouse.MouseOverMobIndex

                        PreviousMagicTargetType = struct.target_type
                        PreviousMagicTargetIndex = struct.targetparam1

                    ElseIf Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY) > 0 Then

                        struct.target_type = EvoLib.Protocol.TargetType.Mob
                        struct.targetparam1 = Mobs.GetMobIDFromPos(GameWindow.MouseX, GameWindow.MouseY)

                    Else

                        If IsPreviousMagicTargetAlive() Then
                            'target previous target
                            struct.target_type = PreviousMagicTargetType
                            struct.targetparam1 = PreviousMagicTargetIndex

                        Else
                            'target coords
                            struct.target_type = EvoLib.Protocol.TargetType.Coords
                            struct.targetparam1 = desc.targetx
                            struct.targetparam2 = desc.targety
                        End If

                    End If

            End Select

            ClientSock.SendData(EvoLib.Protocol.MakePacket(Packet, struct, Globals.Crypto))

        End Sub

        Private Function IsPreviousMagicTargetAlive() As Boolean

            If PreviousMagicTargetIndex > 0 Then

                Select Case PreviousMagicTargetType

                    Case EvoLib.Protocol.TargetType.Mob
                        Dim m As Mobs.Mob
                        m = Mobs.GetMobFromServerID(PreviousMagicTargetIndex)
                        If m.AnimObj Is Nothing Then
                            Return False
                        ElseIf m.VitalPoints.HP <= 0 Then
                            Return False
                        Else
                            Return True
                        End If

                End Select

            Else
                Return False
            End If

        End Function

        Public Function CalcDirection16(magic As Protocol.AddMagic) As Integer

            Dim dx As Integer, dy As Integer

            dx = magic.startx - magic.destx
            dy = magic.starty - magic.desty

            Dim MouseAngle As Single = (Math.Atan2(dx * -1, dy) * (180 / Globals.PI))
            If MouseAngle < 0 Then MouseAngle += 360

            Debug.Print(MouseAngle)

            Dim Direction As Single
            Direction = MouseAngle - 11.25F
            Direction = Math.Floor(Direction / 22.5F)
            'Direction = (Direction - 1) / 2

            Return Direction + 1

        End Function

        Public Sub CalcProjectileSpeedXY(ByRef desc As MagicDesc, magic As Protocol.AddMagic, speed As Long)

            desc.moving = True

            Dim dx As Integer, dy As Integer
            'calculate x/y tiles to travel
            dx = magic.startx - magic.destx
            dy = magic.starty - magic.desty

            Dim dx2 As Integer, dy2 As Integer
            'normalize differences
            If dx < 0 Then dx2 = dx * -1 Else dx2 = dx
            If dy < 0 Then dy2 = dy * -1 Else dy2 = dy
            'calc x/y speed using dx/y ratio
            desc.speedx = (dx2 / (dx2 + dy2)) * speed
            desc.speedy = (dy2 / (dx2 + dy2)) * speed

            'calculate frames before projectile disappears
            If dx > 0 And desc.speedx = 0 Then desc.speedx = 1
            If dx < 0 And desc.speedx = 0 Then desc.speedx = -1
            If dy > 0 And desc.speedy = 0 Then desc.speedy = 1
            If dy < 0 And desc.speedy = 0 Then desc.speedy = -1

            dx2 = 0
            dy2 = 0
            If desc.speedx <> 0.0F And dx <> 0 Then dx2 = (dx * Constants.TileWidth) / desc.speedx
            If desc.speedy <> 0.0F And dy <> 0 Then dy2 = (dy * Constants.TileHeight) / desc.speedy

            If dx < 0 Then desc.speedx *= -1
            If dy < 0 Then desc.speedy *= -1

            If dx2 < 0 Then dx2 *= -1
            If dy2 < 0 Then dy2 *= -1

            If dx2 < dy2 And dx2 <> 0 Then
                desc.deststeps = dx2
            Else
                desc.deststeps = dy2
            End If

        End Sub

    End Module

End Namespace
